/*
 * Copyright 2016 Kevin Herron
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.digitalpetri.modbus.examples.master;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Random;
import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.digitalpetri.modbus.FunctionCode;
import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.WriteMultipleRegistersRequest;
import com.digitalpetri.modbus.requests.WriteSingleRegisterRequest;
import com.digitalpetri.modbus.responses.ModbusResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MasterExample {

    public static void main(String[] args) {
        new MasterExample(100, 100).start();
    }

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

    private final List<ModbusTcpMaster> masters = new CopyOnWriteArrayList<>();
    private volatile boolean started = false;

    private final int nMasters;
    private final int nRequests;

    public MasterExample(int nMasters, int nRequests) {
        this.nMasters = nMasters;
        this.nRequests = nRequests;
    }

    public void start() {
        started = true;

        ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("localhost")
                .setPort(502)
                .build();

        new Thread(() -> {
            while (started) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                double mean = 0.0;
                double oneMinute = 0.0;

                for (ModbusTcpMaster master : masters) {
                    mean += master.getResponseTimer().getMeanRate();
                    oneMinute += master.getResponseTimer().getOneMinuteRate();
                }

                logger.info("Mean rate={}, 1m rate={}", mean, oneMinute);
            }
        }).start();

        for (int i = 0; i < nMasters; i++) {
            ModbusTcpMaster master = new ModbusTcpMaster(config);
            master.connect();

            masters.add(master);

//            byte[] value = new byte[20];
//
//            ByteBuffer values = ByteBuffer.wrap(value);
//            for (int j = 0; j < 10; j++) {
//                short s = (short) (new Random().nextInt(10000));
//                logger.info("随机数：{}", s);
//                s = (short) (s < 0 ? -s : s);
//                values.putShort(s);
//            }
//
//            CompletableFuture<ModbusResponse> future1 = master.sendRequest(new WriteMultipleRegistersRequest(0, 20, value), 0);
//            future1.thenAccept(modbusResponse -> {
//                if (modbusResponse != null) {
//                    FunctionCode functionCode = modbusResponse.getFunctionCode();
//                    logger.info("{}", functionCode);
//                } else {
//                    logger.info("WriteHoldingRegisters return empty ");
//                }
//            });
//            future1.whenComplete((r, e) -> {
//                e.printStackTrace();
//                logger.error(e.getMessage());
//            });

            for (int j = 0; j < nRequests; j++) {
                sendAndReceive(master);
            }
        }
    }

    private void sendAndReceive(ModbusTcpMaster master) {
        if (!started) {
            return;
        }

        CompletableFuture<ReadHoldingRegistersResponse> future =
                master.sendRequest(new ReadHoldingRegistersRequest(1, 10), 1);

        future.thenAccept(response -> {
            if (response != null) {
                int readeable = response.getRegisters().readableBytes();
                ByteBuf byteBuf = response.getRegisters().readBytes(readeable);
                StringJoiner joiner = new StringJoiner(",");
                for (int i = 0; i < readeable; i = i + 2) {
                    joiner.add(String.valueOf(byteBuf.getUnsignedShort(i)));
                }
                logger.info("读取数据 {}", joiner);

                ReferenceCountUtil.release(response);
            }
            scheduler.schedule(() -> sendAndReceive(master), 1, TimeUnit.SECONDS);
        });
        future.whenComplete((r, e) -> {
            e.printStackTrace();
            logger.error(e.getMessage());
        });
    }

    public void stop() {
        started = false;
        masters.forEach(ModbusTcpMaster::disconnect);
        masters.clear();
    }

}
